<?php

/**
 * User: PuLinGao
 * Date: 2014/12/21
 * 处理问卷导入Model
 */
class QuestionSubjectImportModel {

	private $question_id;
	private $totalScore;
	private $buffer;
	private $error;
	private $question_nums;
	private $classify;  //用户保存新模板的一级，二级，三级，四级的对应关系
	private $dataTh; //用于保存表头信息
	private $norm_one = array(); //保存一级指标
	private $norm_two = array(); //保存二级指标
	private $norm_three = array(); //保存三级指标
	public static $tableFieldMap = array(
		'question_num' => '代码',
		'title' => '指标简称',
		'content' => '测评指标',
		'standard_score' => '标准分值'
	);
	
	public static $tableFieldExceptMap = array('实际得分','情景描述',);
	
	// 指标区分判断标识字段（即验证的title中存在这些字段，表示该行 为指标层级）
	public static $norms = array(
		QuestionSubjectClassify::QUESTION_NORM_ONE => '一级指标',
		QuestionSubjectClassify::QUESTION_NORM_TWO => '二级指标',
		QuestionSubjectClassify::QUESTION_NORM_THREE => '三级指标'
	);

	/**
	 * 设置错误信息，方便提示上传者
	 * @param type $errNo 错误单元格编号
	 * @param type $errMsg 错误信息
	 */
	private function setError($errNo, $errMsg) {
		$this->error[] = array(
			'err_no' => $errNo,
			'err_msg' => $errMsg
		);
	}

	/**
	 * 获取错误信息
	 * @return type
	 */
	public function getError() {
		return $this->error;
	}

	/**
	 * 格式化excel数据
	 * @param type $question_id 问卷ID
	 * @param type $excelData	问卷excel数据
	 * @return boolean
	 */
	public function formatExcelData($question_id, $excelData) {
		if (!$question_id) {
			throw new Exception('请指定问卷');
		}
		if (empty($excelData)) {
			throw new Exception('未获取到excel信息');
		}
		$this->question_id = $question_id;

		# 问卷原始数据分割，方便解析
		$splitData = $this->splitData($excelData['data']);
		if (!$splitData) {
			throw new Exception('未能成功区分指标层级，请检查指标格式，正确格式如：“一级指标：A1 网点外部环境及设施（总分：5分）”');
		}
		if (empty($this->norm_three)) {
			throw new Exception('未能检测到三级指标内容，不能正确解析问卷（可能的原因是：基础信息和下面的指标信息中间没有以空行分割），\r\n请下载问卷内容模板仔细对照修改后再上传');
		}
		
		foreach ($splitData as $spKey => $data) {
			if ($spKey == 'base') {
				$this->formatBase($data);
				continue;
			}

			//解析规范数据
			foreach ($data as $n_three => $standD) {
				foreach ($standD as $row => $rowD) {
					//公共项赋值
					$public = array(
						'question_id' => $this->question_id,
						'classify_key' => $n_three,
						'is_basic' => 0,
						'question_type' => 6,
						'question_type_sub' => 0
					);

					//单独从dataTh中取值
					foreach ($this->dataTh as $i => $v) {
						//表头中存在的字段
						$public[$v] = $rowD[$i]['title'];
						
						//记录题目编号
						if ($v == 'question_num') {
							$this->question_nums[$rowD[$i]['coordinate']] = $rowD[$i]['title'];
						}
					}
					
					//如果情景描述中存在值的(有可能是两列，分别获取两列的内容保存到扩展字段，这里使用count加1，是为了排除实际得分列)
					if (count($rowD) > count($this->dataTh)) {
						for ($j = count($this->dataTh)+1; $j < count($rowD); $j++) {
							$forTitle = $rowD[$j]['title'];
							if (!$forTitle) {
								continue;
							}
							
							//是否是选项记录
							$option = $this->filterOption($forTitle);
							if ($option['is_option']) {
								$public['option'] = $option['data'];
								$public['question_type'] = 2;
								$public['question_type_sub'] = 4;
							}else{
								$public['remark'] = join('', $option['data']);
							}
						}
					}
					
					$this->buffer['question_subject'][$row] = $public;
				}
			}
		}
		
		// 题目编号验证
		$uniqueQuestionNum = array_unique($this->question_nums);
		$diffNum = array_diff_assoc($this->question_nums, $uniqueQuestionNum);
		foreach ($this->question_nums as $coor => $num) {
			//去掉题目编号格式的验证，且支持大小写字母
//			if (!preg_match('/^[a-zA-Z][\d\.\d]*$/i', $num)) {
//				$this->setError($coor, "题目编号“{$num}”格式错误，正确格式为 <b>字母，数字，“.”，数字的组合，且必须以字母开头，不能出现下划线，如：A1.2、B4等</b> ，请修改");
//			}
			if (isset($diffNum) && $diffNum[$coor]) {
				$this->setError($coor, "题目编号“" . $num . "”重复，请仔细检查问卷");
			}
		}
		
		// 整理类目标题（classify）
		$this->classify = array_merge($this->norm_one, $this->norm_two, $this->norm_three);
		
	}

	/**
	 * 单独解析基本信息数据
	 * @param type $basedata
	 */
	protected function formatBase($basedata) {
		//解析问卷总分
		$totalScore = array_shift($basedata);
		$last = array_pop($totalScore);
		$this->totalScore = $last['title'];
		
		//去掉base数组第一行内容
		$i = 1;
		foreach ($basedata as $key => $val) {
			$title = $val[0]['title'];
			if (!$title) {
				continue;
			}
			$popTmp = array_pop($val);
			$this->buffer['base'][] = array(
				'question_id' => $this->question_id,
				'is_basic' => 1,
				'question_type' => 1,
				'question_type_sub' => 0,
				'question_num' => "Q" . $i,
				'title' => $title,
				'content' => '',
				'standard_score' => '',
				'content' => '',
				'remark' => $popTmp['title'] ? $popTmp['title'] : ''
			);
			$i++;
		}
	}

	/**
	 * 保存数据，数据入库
	 */
	public function saveExcelData() {
		$questionSubject = new QuestionSubject();
		
		//问卷覆盖上传
		$delStatus = $questionSubject->deleteQuestion($this->question_id);

		# 问卷题目分类信息
		$classify = array();
		foreach ($this->classify as $key => $data) {
			$rowKey = $data['row_key'];
			unset($data['row_key']);
			$classify[$rowKey] = $data;
		}
		$classifyIds = $questionSubject->addQuestionClassify($this->question_id, $classify);
		
		# 插入问题相关
		$subjects = array();
		if ($classifyIds) {
			foreach ($this->buffer as $key => $value) {
				foreach ($value as $val) {
					if ($key == "question_subject") {
						# 分类ID
						$val['classify_id'] = $classifyIds[$val['classify_key']];
						unset($val['classify_key']);
					}
					$subjects[] = $val;
				}
			}
		}
		
		$subjectStatus = $questionSubject->addQuestionSubject($this->question_id, $subjects);

		# 更新问题总数 和 问卷总分
		QuestionBase::model()->updateByPk($this->question_id, array('total_question' => count($subjects), 'total_score' => $this->totalScore));
		return $subjectStatus;
	}

	
	/**
	 * 根据excel中的表头值，获取table表字段，构建插入数据
	 * @param type $excelTitle		验证的数据
	 * @param type $coordinate		验证的数据坐标
	 * @param type $thTitleKey		默认表头验证的标识
	 * @return type
	 * @throws Exception
	 */
	private function getTableField($excelTitle, $coordinate, $thTitleKey) {
		$tableField = '';
		
		// 判断是否所有的表头信息是否合法(通过$thTitleKey)
		if ($thTitleKey) {
			if (!in_array($excelTitle, self::$tableFieldMap)) {
				if (!in_array($excelTitle, self::$tableFieldExceptMap)) {
					throw new Exception('当前的表格坐标“'.$coordinate.'”的数据“'.$excelTitle.'” 命名不规范，无法正确解析对应的问卷内容\r\n 正确的表头名称应该是 “'.join('，', self::$tableFieldMap).'”， 请仔细对照模板正确填写表头名称');
				}
			}
		}
		
		foreach (self::$tableFieldMap as $field => $value) {
			$values = explode(',', $value);
			if (in_array($excelTitle, $values)) {
				$tableField = $field;
			}
		}
		return $tableField;
	}

	/**
	 * 过滤出带有数字标识的选项
	 * @param type $str
	 * @param type $isOption
	 */
	private function filterOption($str, $isOption = true) {
		if (!$str) {
			return false;
		}
		
		$return = array();
		$isOp = false;
		$pattern = '/^(\d+)([\.\、].*)$/im';
		if (!preg_match($pattern, $str)) {
			$return[] = $str;
			
		}else{
			preg_match_all($pattern, $str, $matches);
			if ($isOption) {
				$isOp = true;
				foreach ($matches[1] as $i => $v) {
					$tmp = preg_replace('/\.|\、|\;|\；|\’|\‘|\'|\"|\“|\”/i', '', $matches[2][$i]);
					$return[$v] = $tmp;
				}
				
			} else {
				$return[] = $matches[2][0];
			}
		}
		
		return array(
			'is_option' => $isOp,
			'data' => $return
		);
	}

	/**
	 * 分割出问卷内容中不同的类目
	 * @param type $data
	 * @return type
	 */
	private function splitData($data) {
		if (!$data) {
			return array();
		}
		$splitTmp = array();
		$tmp = array();

		//获取以空行分割的块内容
		foreach ($data as $row => $value) {
			$notNull = false;
			foreach ($value as $col => $v) {
				if (!$v['title']) {
					continue;
				}
				$notNull = true;
			}
			if ($notNull) {
				$tmp[$row] = $value;
			} else {
				$splitTmp[] = $tmp;
				$tmp = array();
				continue;
			}
		}
		if (!empty($tmp)) {
			$splitTmp[] = $tmp;
		}

		//区分出"基础信息"对应的题目
		$real['base'] = $splitTmp[0];
		unset($splitTmp[0]);

		//区分多级指标
		$real['norm'] = $this->splitMultiNorms(array_pop($splitTmp));

		return $real;
	}

	/**
	 * 多指标解析
	 * @param type $multinorms
	 */
	public function splitMultiNorms($multinorms) {
		if (!$multinorms) {
			return array();
		}

		// 起始key
		$realData = array();
		$startKey = 0;
		$tmpMulti = $multinorms;
		$thTitleKey = true;  //验证表头使用的标识
		foreach ($multinorms as $key => $one) {
			foreach ($one as $chrK => $value) {
				if (!$value['title']) {
					continue;
				}
				$title = $value['title'];
				
				//获取datath
				$field = $this->getTableField($title, $value['coordinate'], $thTitleKey);
				if ($field) {
					$this->dataTh[$chrK] = $field;
					continue;
				}
				
				$thTitleKey = false;  # 验证表头后，将其标识置为false;
				
				//获取多级指标的内容
				foreach (self::$norms as $nok => $nod) {
					$tmp = array();
					if (false !== strstr($title, $nod)) {
						$unsetKey = $this->calcNorms($title, $nok, $key);
						if (in_array($nok, array_flip(self::$norms))) {
							$startKey = $key;
						}
						break;
					}
				}
				
			}
			
			if ($startKey == $key) {
				continue;
			}
			$startKey and $realData[$startKey][$key] = $tmpMulti[$key];
		}
		
		return $realData;
	}

	/**
	 * 计算问卷中的各级指标的对应关系
	 * 算法：获得当前的$normTitle属于哪一组指标，每次获取上级指标数组的最后一个，将最后一个的key值作为当前$normTitle的parent_id
	 * @param type $normTitle	指标的名称
	 * @param type $normKey		问卷中指标所在的key值，是一级还是二级
	 * @param type $normRow		问卷中指标所在的行
	 * @return type
	 */
	protected function calcNorms($normTitle, $normKey, $normRow) {
		if (!$normTitle || !$normKey) {
			$this->classify = array();
		}

		$tmp = array(
			'classify_name' => $normTitle,
			'question_id' => $this->question_id,
			'classify_type' => $normKey,
			'create_time' => time(),
			'status' => 1,
			'row_key' => $normRow
		);

		switch ($normKey) {
			case QuestionSubjectClassify::QUESTION_NORM_ONE:
				$tmp['parent_id'] = 0;
				$this->norm_one[$normRow] = $tmp;
				break;

			case QuestionSubjectClassify::QUESTION_NORM_TWO:
				$normOne = $this->norm_one;
				$one = $normOne ? array_pop($normOne) : '';
				$tmp['parent_id'] = (isset($one['row_key']) && $one['row_key']) ? $one['row_key'] : 0;
				$tmp['parent_name'] = (isset($one['classify_name']) && $one['classify_name']) ? $one['classify_name'] : '';
				$this->norm_two[$normRow] = $tmp;
				break;

			case QuestionSubjectClassify::QUESTION_NORM_THREE:
				$normTwo = $this->norm_two;
				$one = $normTwo ? array_pop($normTwo) : '';
				if (!$one) {
					$normOne = $this->norm_one;
					$one = $normOne ? array_pop($normOne) : '';
				}

				$tmp['parent_id'] = (isset($one['row_key']) && $one['row_key']) ? $one['row_key'] : 0;
				$tmp['parent_name'] = (isset($one['classify_name']) && $one['classify_name']) ? $one['classify_name'] : '';
				$this->norm_three[$normRow] = $tmp;
				break;

			default:
				break;
		}
		return $normRow;
	}

}
